#region Usings

using System;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json;
using NHibernate.Proxy;

#endregion

namespace Karma.Framework.Core.Serialization.Json
{
    ///<summary>
    /// Convertidor utilizado para soportar los proxies de NHibernate.
    ///</summary>
    public class NHibernateProxyJsonSerializer : JsonSerializer
    {
        private static readonly MemberInfo[] _nHibernateProxyInterfaceMembers =
            typeof (INHibernateProxy).GetMembers();

        protected override List<MemberInfo> GetSerializableMembers(Type objectType)
        {
            var members = base.GetSerializableMembers(objectType);

            members.RemoveAll(
                memberInfo => (isMemberPartOfNHibernateProxyInterface(memberInfo)) ||
                              (isMemberDynamicProxyMixin(memberInfo)) ||
                              (isMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) ||
                              (isMemberInheritedFromProxySuperclass(memberInfo, objectType)));

            return members;
        }

        private static bool isMemberPartOfNHibernateProxyInterface(MemberInfo memberInfo)
        {
            return Array.Exists(
                _nHibernateProxyInterfaceMembers,
                delegate(MemberInfo mi) { return memberInfo.Name == mi.Name; });
        }

        private static bool isMemberDynamicProxyMixin(MemberInfo memberInfo)
        {
            return memberInfo.Name == "__interceptors";
        }

        private static bool isMemberMarkedWithIgnoreAttribute(
            MemberInfo memberInfo, Type objectType)
        {
            return objectType.BaseType.GetMember(memberInfo.Name).GetType().GetCustomAttributes(
                       typeof (JsonIgnoreAttribute), true).Length > 0;
        }

        private static bool isMemberInheritedFromProxySuperclass(
            MemberInfo memberInfo, Type objectType)
        {
            return memberInfo.DeclaringType != objectType;
        }
    }
}